<html lang="en">

<head>
  <meta charset="UTF-8">
  <meta http-equiv="X-UA-Compatible" content="IE=edge">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>Document</title>
</head>

<body>
  <script>
    function dp(s, p) {
      /**
       * 该方案使用动态规划的方式，构造一个[p.length+1,s.length+1]的二维矩阵，
       * 行从1开始对应匹配字符串字符索引，列从1开始对应原串字符索引，之所以行
       * 列都加了1个长度，是因为0行/0列可代表匹配字符串/原串为空的情况， 也
       * 方便后续处理，该矩阵[i,j]的状态代表：匹配字符从0到i-1处能否与原串字
       * 符从0到j-1匹配，匹配成功为1，失败为0。如果矩阵最后一个状态为1，则证明
       * 匹配字符串与原串匹配成功。
       */

      // 如果原串等于匹配字符串，直接返回true
      if (s === p) return true;
      // 定义dp矩阵(行从1开始对应匹配字符串字符索引，列从1开始对应原串字符索引)
      // 之所以行列都加了1个长度，是因为0行/0列可代表匹配字符串/原串为空的情况，
      // 也方便后续处理
      let dpArray = new Array(p.length + 1).fill(0).map(() => new Array(s.length + 1).fill(0));
      // 当两者都为空字符串时，此时两字符匹配，因此初始化[0][0]位置为1
      dpArray[0][0] = 1;
      // 遍历匹配字符串每个字符
      for (let i = 1; i <= p.length; i++) {
        // 当匹配字符为"*"时，由于"*"可匹配零个字符，因此其
        // ([i][0])位置状态应为上一匹配字符0列([i-1][0])位置状态
        if (p[i - 1] === "*") dpArray[i][0] = dpArray[i - 1][0];
        // 遍历原串每个字符
        for (let j = 1; j <= s.length; j++) {
          // 如果当前匹配字符等于"*"，当"*"匹配0个字符时，其状态应等于上一个匹配字符
          // 匹配原串当前字符的状态即dpArray[i-1][j]，当"*"匹配n个字符时，其状态应
          // 等于当前匹配字符匹配原串上一字符的状态即dpArray[i][j-1]，因此此处状态
          // 为两者或的结果
          if (p[i - 1] === "*") dpArray[i][j] = dpArray[i - 1][j] || dpArray[i][j - 1];
          // 当前匹配字符匹配原串成功，当前状态则等于上一个匹配字符/原串字符匹配的情况
          else if (p[i - 1] === "?" || p[i - 1] === s[j - 1]) dpArray[i][j] = dpArray[i - 1][j - 1];
        }
      }
      // 如果最后一个字符匹配成功，则证明匹配字符串能匹配原串
      return dpArray[p.length][s.length] == 1;
    }
    console.log(dp(s = "aa", p = "*"))
  </script>
</body>

</html>